package sc.server.world;

import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import sc.datainterchange.CodedOutputBuffer;
import sc.server.Database;

public abstract class Container {
	public static Map<Integer, List<Container>> containers = new HashMap<Integer, List<Container>>();

	private int id;
	private int parent;
	private int x;
	private int y;
	private int z;

	/**
	 * Creates an instance of Container.
	 */
	public Container(int id) {
		this.id = id;
		this.parent = -1;
		this.x = 0;
		this.y = 0;
		this.z = 0;
		List<Container> objectlist = new ArrayList<Container>();
		objectlist.add(this);
		Container.containers.put(id, objectlist);
	}

	/**
	 * Gets the object type ID of the Container.
	 * 
	 * @return object type ID of the Container
	 */
	abstract public int getObjectTypeID();

	/**
	 * Gets the ID of the Container.
	 * 
	 * @return ID of the Container
	 */
	public int getID() {
		return id;
	}

	/**
	 * Puts the specified Container into this Containers contents.
	 * 
	 * If object already has a parent, it is first removed from its contents.
	 * 
	 * @param object
	 */
	public void add(Container object) {
		if (object.parent > -1)
			Container.containers.get(object.parent).remove(object);
		object.parent = id;
		Container.containers.get(id).add(object);
	}

	/**
	 * Removes the specified Thing from this Objects contents.
	 * 
	 * @param object
	 */
	public void remove(Container object) {
		Container.containers.get(id).remove(object);
	}

	/**
	 * Returns the Tile containing this Container.
	 * 
	 * @return the Tile container
	 * @throws SQLException
	 */
	public Tile getTile() throws SQLException {
		int tilex = (this.x / Tile.WIDTH) + Tile.HALFMAPSIZE;
		int tilez = (this.z / Tile.WIDTH) + Tile.HALFMAPSIZE;
		return Tile.get(tilex, tilez);
	}

	/**
	 * Sets x-axis position.
	 * 
	 * @param x
	 *            x-axis position
	 */
	public void setX(int x) {
		this.x = x;
	}

	/**
	 * Returns x-axis position.
	 * 
	 * @return x-axis position
	 */
	public int getX() {
		return this.x;
	}

	/**
	 * Sets y-axis position.
	 * 
	 * @param y
	 *            y-axis position
	 */
	public void setY(int y) {
		this.y = y;
	}

	/**
	 * Returns y-axis position.
	 * 
	 * @return y-axis position
	 */
	public int getY() {
		return this.y;
	}

	/**
	 * Sets z-axis position.
	 * 
	 * @param z
	 *            z-axis position
	 */
	public void setZ(int z) {
		this.z = z;
	}

	/**
	 * Returns z-axis position.
	 * 
	 * @return z-axis position
	 */
	public int getZ() {
		return this.z;
	}

	/**
	 * Sets the parent Container.
	 * 
	 * @param parent
	 *            Parent container
	 */
	public void setParent(int parent) {
		this.parent = parent;
	}

	/**
	 * Returns the parent Container.
	 * 
	 * @return parent Container
	 */
	public int getParent() {
		return this.parent;
	}

	/**
	 * Writes this Containers information to a CodedOutputBuffer.
	 * 
	 * @throws IOException
	 */
	public void write(CodedOutputBuffer codedoutput) throws IOException {
		codedoutput.writeRawVarint32(this.getObjectTypeID());
		codedoutput.writeRawVarint32(this.getID());
		codedoutput.writeRawVarint32(Container.containers.get(this.parent).get(
				0).getObjectTypeID());
		codedoutput.writeRawVarint32(this.parent);
		codedoutput.writeRawVarint32(this.x);
		codedoutput.writeRawVarint32(this.y);
		codedoutput.writeRawVarint32(this.z);
	}

	/**
	 * Creates a new Container entry in the database.
	 * 
	 * @param type
	 *            Object type
	 * @return Container id
	 * @throws SQLException
	 */
	public static int create(int type) throws SQLException {
		PreparedStatement stmt = Database.getContainerCreate();
		stmt.setInt(1, type);
		stmt.setNull(2, Types.INTEGER);
		stmt.setInt(3, 0);
		stmt.setInt(4, 0);
		stmt.setInt(5, 0);
		stmt.executeUpdate();
		ResultSet results = stmt.getGeneratedKeys();

		if (results.next())
			return results.getInt(1);

		return -1;
	}

	/**
	 * Writes this Containers information to the database.
	 * 
	 * @param d
	 *            Database instance
	 * @throws SQLException
	 */
	public void save(Database d) throws SQLException {
		PreparedStatement stmt = Database.getContainerSave();
		stmt.setInt(1, parent);
		stmt.setInt(2, x);
		stmt.setInt(3, y);
		stmt.setInt(4, z);
		stmt.setInt(5, id);
		stmt.executeUpdate();
	}

	/**
	 * Reads this Containers information from the database.
	 * 
	 * @param d
	 *            Database instance
	 * @throws SQLException
	 */
	public void read(Database d) throws SQLException {
		PreparedStatement stmt = Database.getContainerRead();
		stmt.setInt(1, id);
		ResultSet rs = stmt.executeQuery();

		if (rs.next()) {
			parent = rs.getInt(3);
			x = rs.getInt(4);
			y = rs.getInt(5);
			z = rs.getInt(6);
		}
	}
}
